package robot;

//imports
import graphics.Point;
import gui.Client;
import gui.WaitingFrame;
import javax.swing.JFrame;

/**
 * Subclass of robot, maintains properties and methods used by virtual robots
 *
 * @author Gabriel Loewen
 * @author James Weston
 * @author Jackie O'Quinn
 */
public class VirtualRobot extends Robot {

    /**
     * Boolean value for deferred thread cancellation
     */
    private boolean cancelMovement;

    /**
     * Constructor, accepts a robot name, location, and heading
     * @param name      Name of the robot
     * @param point     Position of the robot in the grid in (X,Y) format
     * @param head      The initial heading of the robot (N, E, S, or W)
     */
    public VirtualRobot(String name, Point point, char head) {
        super(name, point, head, true);
        cancelMovement = false;
    }

    /**
     * The process to move a robot requires three threads which are
     * defined by the following:
     * WaitThread - Instantiates a jframe which acts as a progress monitor
     * while the behavior generates a path.
     *
     * PathGenerator - Is called by the wait thread to generate a path from
     * the current behavior
     *
     * MoveThread - Gets the generated path from the behavior and operates
     * the movement of the robot according to the path, or for non-path-driven
     * behaviors a heuristical method is usually used to generate the next
     * move.
     * 
     * Uses a thread to generate a path from the behavior that the robot has
     * been assigned and then moves the robot.  The reason why so many threads
     * are being used is because without these threads the dialog box containing
     * the status of the behavior would be blocked from executing.
     *
     * @param goal      The goal location that the robot is instructed to move to.
     */
    @Override
    public void move(final String goal) {
        MoveThread m = new MoveThread();
        PathGenerator p = new PathGenerator(goal, m);
        WaitThread w = new WaitThread(p);
        new Thread(w).start();
    }

    /**
     * Enable deferred thread cancellation
     */
    public void cancel() {
        cancelMovement = true;
    }

    /**
     * Generates a path from the behavior
     */
    class PathGenerator implements Runnable {

        private String goal;
        MoveThread move;
        private boolean end = false;

        public PathGenerator(String g, MoveThread m) {
            move = m;
            goal = g;
        }

        public void cancel() {
            end = true;
        }

        public void run() {
            behavior.getPath(goal, end);
            try {
                Thread.sleep(1);
            } catch (Exception ex) {
            }
            if (!end) {
                new Thread(move).start();
            }
        }
    }

    /**
     * Moves the robot according to the generated path
     */
    class MoveThread implements Runnable {

        public MoveThread() {
        }

        public void run() {
            while (behavior.hasMove() && !cancelMovement) {
                if (behavior.isValidMove()) {
                    update(behavior.nextMove());
                    getArena().getGrid().redrawRobot(oldLocation, location);
                    //printPositionMatrix(getPositionMatrix());
                    
                    try {
                        Thread.sleep(Client.robotDelay);
                    } catch (Exception ex) {
                    }
                } else {
                    System.out.println(getID() + " is deadlocked");
                }
            }
        }
    }

    /**
     * Waits for the path to be generated using a gui
     */
    class WaitThread implements Runnable {

        private WaitingFrame waiting;
        private PathGenerator path;
        private boolean prematureEnd;

        public WaitThread(PathGenerator p) {
            waiting = new WaitingFrame();
            behavior.resetProgress();
            waiting.setBehavior(behavior);
            waiting.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
            waiting.setLocationRelativeTo(null);
            waiting.setVisible(true);
            prematureEnd = false;
            path = p;
        }

        public void run() {
            new Thread(path).start();
            while (behavior.getProgress() < 100 && !prematureEnd && !cancelMovement) {
                waiting.progress.setValue((int) behavior.getProgress());
                if (waiting.exit) {
                    path.cancel();
                    prematureEnd = true;
                }
                try {
                    Thread.sleep(1);
                } catch (Exception ex) {
                }
            }
            waiting.setVisible(false);
            waiting.dispose();
        }
    }
}
